package es.ucm.fdi.xtremeaudio.modelo.dao;

import java.io.File;
import java.io.PrintStream;
import java.util.Map;

import javazoom.jlgui.basicplayer.BasicController;
import javazoom.jlgui.basicplayer.BasicPlayer;
import javazoom.jlgui.basicplayer.BasicPlayerEvent;
import javazoom.jlgui.basicplayer.BasicPlayerException;
import javazoom.jlgui.basicplayer.BasicPlayerListener;

/** 
 * @author IS-G7B
 * <br/><br/>
 * Esta clase implementa un reproductor simple basado en BasicPlayer.
 * BasicPlayer funciona sobre JavaSound SPIs para soportar 
 * multiples formatos de audio. Basicamente, JavaSound soporta
 * WAV, AU y AIFF. Las librerias MP3 SPI y Vorbis SPI 
 * ayudan a dar soporte para MP3 y Ogg Vorbis. 
 */
public class Player implements BasicPlayerListener { 
	
	/**
	 * <i> out </i> - PrintStream de salida
	 * <i> control </i> - Controlador de la reproducion
	 */
	private PrintStream salida = null;
	private BasicPlayer control;
	private double volumen;
	private boolean mute;
	private int bytesRead;
	private long microsegundos;

	/**
	 * Contructora por defecto.
	 * 
	 * Establece la salida estandar para imprimir,
	 * el control del reproductor y la ganancia 
	 * de volumen y balance a un nivel intermedio
	 */
	public Player(){
		salida = System.out; 
		control = new BasicPlayer();
		control.addBasicPlayerListener(this);
		setGanancia(0.5);
		mute = false;
		bytesRead = 0;
		microsegundos = 0;
		try {
			control.setPan(0.0);
		} catch (BasicPlayerException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Instancia el reproductor con un archivo dado.
	 * 
	 * @param sArchivo Path del archivo.
	 */
	public void open(String sArchivo) {
		try {
			control.open(new File(sArchivo));
		} catch (BasicPlayerException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Comienza a reproducir la cancion asociada.
	 */
	public void play(){
		try 
		{
			control.play();
			control.setGain(volumen);
			if (mute)
			{
				control.setGain(0.0);
			}
		} catch (BasicPlayerException e) {
			e.printStackTrace();
		}
		
	}

	/**
	 * Continua la reproduccion pausada
	 */
	public void resume() {
		try {
			control.resume();
		} catch (BasicPlayerException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Detiene la reproduccion.
	 */
	public void stop(){	
		try {
			control.stop();
		} catch (BasicPlayerException e) {
			e.printStackTrace();
		}	
	}

	/**
	 * Pausa la reproduccion.
	 * 
	 */
	public void pause() {
		try {
			control.pause();
		} catch (BasicPlayerException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Establece el estado de la reproduccion en el byte indicado por bytes
	 * 
	 * @param bytes numero de byte
	 */	
	public void seek(long bytes)
	{
		try {
			control.seek(bytes);
			control.setGain(volumen);
		} catch (BasicPlayerException e) {
			e.printStackTrace();
		}
		
	}
	/**
	 * Devuelve el estado actual de la reproduccion,
	 * representado por un entero.
	 * 
	 * @return Estado actual del reproductor.
	 */
	public int getStatus()
	{
		return control.getStatus();
	}

	/**
	 * Establece la ganancia de volumen
	 * en un rango entre 0.0 y 1.0.
	 * 
	 * @param vol Ganancia nueva (entre 0.0 y 1.0)
	 */
	public void setGanancia(double vol) {
		try {
			volumen = vol;
			if (!mute){
				
				control.setGain(volumen);
			}
		} catch (BasicPlayerException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Permite silenciar la reproduccion .
	 */
	public void mute() {
		if (mute){
			try {
				control.setGain(volumen);
			} catch (BasicPlayerException e) {
				e.printStackTrace();
			}
		} else {
			try {
				control.setGain(0.0);
			} catch (BasicPlayerException e) {
				e.printStackTrace();
			}
		}
		mute = !mute;
	}
	
	/**
	 * Repite una cancion o toda una lista.
	 * Si no, se desactiva.
	 * 
	 * Ciclo: OFF-Uno-Todo
	 */
	public void repetir() {
		
	}

	/**
	 * Reproduce una cancion aleatoria de la lista.
	 */
	public void aleatorio() {
		
	}
	
	/**
	 * Imprime las propiedades del archivo abierto.
	 * El mapa de propiedades incluye, entre otros,
	 * bitrate, duracion, frecuencia, canales, numero de frames, etc.
	 *
	 * @param stream (File, URL o InputStream).
	 * @param properties Mapa de propiedades.
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void opened(Object stream,  Map props)
	{
		display("Abierto : " + props.toString());
	}

	/** Este metodo es llamado varias veces en un segundo.
	 * 
	 * El mapa de propiedades incluye, entre otros,
	 * bitrate, duracion, frecuencia, canales, numero de frames, etc.
	 *
	 * @param bytesRead Bytes leidos en el archivo actual.
	 * @param microsegs Microsegundos transcurridos (<b>Reseteado tras hacer un seek!!!</b>).
	 * @param pcmData Datos PCM.
	 * @param props Mapa de propiedades.
	 */

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void progress(int bytesRead, long microsegs, byte[] pcmData, Map props)
	{
		display("Progreso : " + props.toString());
		setBytesRead(bytesRead);
		if (microsegs < microsegundos){ //En este caso se resetearon
			setMicrosegundos(microsegundos + microsegs);
		} else {
			setMicrosegundos(microsegs);
		}
	}

	/**
	 * Notificacion para eventos del reproductor
	 * (play, pausa, etc.).
	 *
	 * @param event Evento que causa la notificacion
	 */
	public void stateUpdated(BasicPlayerEvent event)
	{
		display("Estado actualizado : " + event.toString());
	}

	/**
	 * Establece un manejador del BasicPlayer a trav�s de un es.ucm.fdi.xtremeaudio.controlador
	 * @param controller El manejador (es.ucm.fdi.xtremeaudio.controlador)
	 */ 
	public void setController(BasicController controller)
	{
		display("setController : " + controller);
	}

	/**
	 * Imprime un mensaje por la salida indicada en <b> out </b>.
	 * @param msg Mensaje a mostrar
	 */
	private void display(String msg)
	{
		if (salida != null){
			salida.println(msg);
		}
	}

	/*--- Accesoras y mutadoras ---*/
	/**
	 * Accesora para la salida establecida
	 * 
	 * @return PrintStream de salida
	 */
	public PrintStream getSalida() {
		return salida;
	}

	/**
	 * Mutadora para establecer la salida
	 * 
	 * @param out Nueva salida
	 */
	public void setSalida(PrintStream salida) {
		this.salida = salida;
	}

	/**
	 * Accesora para obtener el es.ucm.fdi.xtremeaudio.controlador del reproductor
	 * 
	 * @return BasicPlayer es.ucm.fdi.xtremeaudio.controlador
	 */
	public BasicPlayer getControl() {
		return control;
	}

	/**
	 * Mutadora para establecer el es.ucm.fdi.xtremeaudio.controlador del reproductor
	 * 
	 * @param control Nuevo es.ucm.fdi.xtremeaudio.controlador
	 */
	public void setControl(BasicPlayer control) {
		this.control = control;
	}

	/**
	 * Accesora para conocer el estado del mute 
	 * 
	 * @return Estado de silencio actual
	 */
	public boolean isMute() {
		return mute;
	}

	/**
	 * @param mute the mute to set
	 */
	public void setMute(boolean mute) {
		this.mute = mute;
	}
	
	/**Accesora para conocer el numero de bytes leidos
	 * 
	 * @return the bytesRead
	 */
	public int getBytesRead() {
		return bytesRead;
	}
	
	/**
	 * Mutadora para el numero de bytes leidos
	 * 
	 * @param bytes Nuevo numero de bytes
	 */
	public void setBytesRead(int bytes) {
		bytesRead = bytes;
	}

	/**
	 * @return the volumen
	 */
	public double getVolumen() {
		return volumen;
	}

	/**
	 * @param volumen the volumen to set
	 */
	public void setVolumen(double volumen) {
		this.volumen = volumen;
	}

	/**
	 * @return the microsegundos
	 */
	public long getMicrosegundos() {
		return microsegundos;
	}

	/**
	 * @param microsegundos the microsegundos to set
	 */
	public void setMicrosegundos(long microsegundos) {
		this.microsegundos = microsegundos;
	}

}